/*
 * @copyright Copyright (c) 2010 Laboratório de Educação Cerebral. (http://www.educacaocerebral.com.br)
 *
 * This file is part of SoftVFC.
 *
 * SoftVFC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SoftVFC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SoftVFC.  If not, see <http://www.gnu.org/licenses/>.
 *
 * *********************
 *
 * Este arquivo é parte do programa SoftVFC.
 *
 * SoftVFC é um software livre; você pode redistribui-lo e/ou
 * modifica-lo dentro dos termos da Licença Pública Geral GNU como
 * publicada pela Fundação do Software Livre (FSF); na versão 3 da
 * Licença.
 *
 * Este programa é distribuido na esperança que possa ser util,
 * mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO a qualquer
 * MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a
 * Licença Pública Geral GNU para maiores detalhes.
 *
 * Você deve ter recebido uma cópia da Licença Pública Geral GNU
 * junto com este programa, se não, acesse no website oficial:
 * http://www.gnu.org/licenses/gpl.html
 *
 */
package org.softvfc.daq.options;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.StatusDisplayer;
import org.openide.util.NbPreferences;
import org.softvfc.dialog.notify.NotifyDialog;
import org.softvfc.dialog.notify.NotifyType;

public final class DriversPanel extends javax.swing.JPanel {

    private final DriversOptionsPanelController controller;
    private final Mixer.Info[] allMixer = AudioSystem.getMixerInfo();
    private Line.Info[] allTLineInfos = null;
    private Mixer mixer = null;

    DriversPanel(DriversOptionsPanelController controller) {
        this.controller = controller;
        initComponents();
        controller.changed();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTabbedPane1 = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jcbMixerDriver = new javax.swing.JComboBox();
        jLabel2 = new javax.swing.JLabel();
        jcbLines = new javax.swing.JComboBox();
        jLabel3 = new javax.swing.JLabel();
        jcbAudioFormat = new javax.swing.JComboBox();
        jbTest = new javax.swing.JButton();
        jtbUseDefault = new javax.swing.JToggleButton();

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jLabel1.text")); // NOI18N

        jcbMixerDriver.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                jcbMixerDriverChangeMixerDriver(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jLabel2.text")); // NOI18N

        jcbLines.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                jcbLinesChangeTargetLine(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jLabel3.text")); // NOI18N

        jcbAudioFormat.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                jcbAudioFormatChangeAudioFormat(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jbTest, org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jbTest.text")); // NOI18N
        jbTest.setPreferredSize(new java.awt.Dimension(60, 29));
        jbTest.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jbTestActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jtbUseDefault, org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jtbUseDefault.text")); // NOI18N
        jtbUseDefault.setRolloverEnabled(false);
        jtbUseDefault.setSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/softvfc/daq/options/emblem-default.png"))); // NOI18N
        jtbUseDefault.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jtbUseDefaultAction(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(jLabel2)
                            .addComponent(jLabel3))
                        .addGap(63, 63, 63)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jcbAudioFormat, 0, 474, Short.MAX_VALUE)
                            .addComponent(jcbLines, javax.swing.GroupLayout.Alignment.LEADING, 0, 474, Short.MAX_VALUE)
                            .addComponent(jcbMixerDriver, javax.swing.GroupLayout.Alignment.LEADING, 0, 474, Short.MAX_VALUE)))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jtbUseDefault, javax.swing.GroupLayout.PREFERRED_SIZE, 214, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 358, Short.MAX_VALUE)
                        .addComponent(jbTest, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jcbMixerDriver, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(jcbLines, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jcbAudioFormat, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel3))
                .addGap(18, 18, 18)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jtbUseDefault)
                    .addComponent(jbTest, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(101, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jPanel1.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/softvfc/daq/options/audio-input-microphone.png")), jPanel1); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 668, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 301, Short.MAX_VALUE)
        );

        jTabbedPane1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DriversPanel.class, "DriversPanel.jTabbedPane1.AccessibleContext.accessibleName")); // NOI18N
    }// </editor-fold>//GEN-END:initComponents

    private void jcbMixerDriverChangeMixerDriver(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_jcbMixerDriverChangeMixerDriver

        if(evt.getSource() == jcbMixerDriver){

            jcbLines.removeAllItems();
            jcbAudioFormat.removeAllItems();
            jcbLines.setEnabled(false);
            jcbAudioFormat.setEnabled(false);
            try{
                if(jcbMixerDriver.getSelectedIndex()>=0){
                    mixer = AudioSystem.getMixer(allMixer[jcbMixerDriver.getSelectedIndex()]);
                    allTLineInfos = mixer.getTargetLineInfo();
                    for (int j=0; j<allTLineInfos.length; j++) {
                        jcbLines.addItem(allTLineInfos[j]);
                        jcbLines.setEnabled(true);
                    }
                }
            }catch(Exception ex){
                 ex.printStackTrace();
            }
        }
    }//GEN-LAST:event_jcbMixerDriverChangeMixerDriver

    private void jcbLinesChangeTargetLine(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_jcbLinesChangeTargetLine

        if(evt.getSource() == jcbLines){

            try{
                if(allMixer.length >= 0 && jcbMixerDriver.getSelectedIndex() >= 0){
                    mixer = AudioSystem.getMixer(allMixer[jcbMixerDriver.getSelectedIndex()]);
                    allTLineInfos = mixer.getTargetLineInfo();
                    if(allTLineInfos.length >= 0 && jcbLines.getSelectedIndex() >= 0){
                        DataLine.Info infoDataLine = null;
                        try{
                            infoDataLine = ((DataLine.Info)allTLineInfos[jcbLines.getSelectedIndex()]);
                        }catch(Exception ex){
                            //ex.printStackTrace();
                        }
                        if(infoDataLine!=null){
                            for(AudioFormat audioFormat : infoDataLine.getFormats()){
                                jcbAudioFormat.addItem(audioFormat);
                                jcbAudioFormat.setEnabled(true);
                            }
                        }
                    }
                }
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }//GEN-LAST:event_jcbLinesChangeTargetLine

    private void jcbAudioFormatChangeAudioFormat(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_jcbAudioFormatChangeAudioFormat


    }//GEN-LAST:event_jcbAudioFormatChangeAudioFormat

    private void jtbUseDefaultAction(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jtbUseDefaultAction
        if(evt.getSource() == jtbUseDefault){
            if(jtbUseDefault.isSelected() ){
                jcbMixerDriver.removeAllItems();
                jcbLines.removeAllItems();
                jcbAudioFormat.removeAllItems();
                jcbMixerDriver.setEnabled(false);
                jcbLines.setEnabled(false);
                jcbAudioFormat.setEnabled(false);
                jtbUseDefault.setSelected(true);
            }else{
                jcbMixerDriver.setEnabled(true);
                for(int m = 0; m < allMixer.length; m++){
                    jcbMixerDriver.addItem(allMixer[m].getName());
                }

            }
        }
    }//GEN-LAST:event_jtbUseDefaultAction

    public AudioFormat getDefaultFormat() {
        return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100.0f, 16, 2, 4, 44100.0f, false);
    }

    private void jbTestActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jbTestActionPerformed

        String testResult = "";
        if(jtbUseDefault.isSelected()){
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, getDefaultFormat());
            if(info.isFormatSupported(getDefaultFormat()) ){
                testResult += "Format:: This Default Format is Supported by Audio System:\n " + getDefaultFormat() + "\n";
            }else{
                testResult += "Format:: This Default Format is Supported by Audio System:\n " + getDefaultFormat() + "\n";
            }
            if (AudioSystem.isLineSupported(info)) {
                testResult += "Target Line:: Default Line is Supported by Audio System:\n " + info + "\n";
            }else{
                testResult += "Target Line:: Default Line is not Supported by Audio System:\n " + info + "\n";
            }
            try {
                TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
                line.open(getDefaultFormat(), line.getBufferSize());
                line.close();
            } catch (LineUnavailableException ex) {
                testResult += "Line Availability:: Line is Unavailable!\n ";
            } catch (SecurityException ex) {
                testResult += "Line Security:: Line is not Security!\n ";
            } catch (Exception ex) {
                testResult += "Line Exception:: Line is not TDL!\n ";
            }

        }else{

            if( (jcbMixerDriver.getSelectedIndex() >= 0) && (jcbLines.getSelectedIndex()>=0) && (jcbAudioFormat.getSelectedIndex()>=0) ){
                AudioFormat format = (AudioFormat)jcbAudioFormat.getSelectedItem();
                DataLine.Info info = new DataLine.Info(allTLineInfos[jcbLines.getSelectedIndex()].getLineClass(), format);
                if(info.isFormatSupported(format) ){
                    testResult += "Format:: This Format is Supported by Audio System:\n " + format + "\n";
                }else{
                    testResult += "Format:: This Format is Supported by Audio System:\n " + format + "\n";
                }
                if (AudioSystem.isLineSupported(info)) {
                    testResult += "Target Line:: This Line is Supported by Audio System:\n " + info + "\n";
                }else{
                    testResult += "Target Line:: This Line is not Supported by Audio System:\n " + info + "\n";
                }
                try{

                    TargetDataLine line = (TargetDataLine) mixer.getLine( info );
                    line.open(format, line.getBufferSize());
                    line.close();
                } catch (LineUnavailableException ex) {
                    testResult += "Line Availability:: Line is Unavailable!\n ";

                } catch (SecurityException ex) {
                    testResult += "Line Security:: Line is not Security!\n ";

                } catch (Exception ex) {
                    testResult += "Line Exception:: Line is not TDL!\n ";

                }
            }else{

                testResult += "Mixer Driver:: This configuration is not valid!\n ";

            }
            
        }

        NotifyDescriptor nd = new NotifyDescriptor(
                testResult,
                "Test Mixer Result",
                NotifyDescriptor.PLAIN_MESSAGE,
                NotifyDescriptor.INFORMATION_MESSAGE,
                null,
                NotifyDescriptor.INFORMATION_MESSAGE
        );


        if (DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.OK_OPTION) {

        }
    }//GEN-LAST:event_jbTestActionPerformed

    void load() {


        jtbUseDefault.setSelected(NbPreferences.forModule(DriversPanel.class).getBoolean("Default", true));
        if(jtbUseDefault.isSelected()){
            jcbMixerDriver.setEnabled(false);
            jcbLines.setEnabled(false);
            jcbAudioFormat.setEnabled(false);
            jcbMixerDriver.removeAllItems();
            jcbLines.removeAllItems();
            jcbAudioFormat.removeAllItems();
            StatusDisplayer.getDefault().setStatusText("Default Driver Selected...");
        }else{
            try{
                jcbMixerDriver.removeAllItems(); 
                for(int m = 0; m < allMixer.length; m++){
                    jcbMixerDriver.addItem(allMixer[m].getName());
                    jcbMixerDriver.setEnabled(true);
                }

                int storeMixerDriver = NbPreferences.forModule(DriversPanel.class).getInt("MixerDriver", 0);
                if(storeMixerDriver>=0){
                    jcbMixerDriver.setSelectedIndex(storeMixerDriver);
                    mixer = AudioSystem.getMixer(allMixer[jcbMixerDriver.getSelectedIndex()]);
                    allTLineInfos = mixer.getTargetLineInfo();
                    jcbLines.removeAllItems();
                    for (int j=0; j<allTLineInfos.length; j++) {
                        jcbLines.addItem(allTLineInfos[j]);
                        jcbLines.setEnabled(true);
                    }

                    int storeTargetLine = NbPreferences.forModule(DriversPanel.class).getInt("TargetLine", 0);
                    if(storeTargetLine>=0){
                        jcbLines.setSelectedIndex(storeTargetLine);
                        jcbAudioFormat.removeAllItems();
                        DataLine.Info infoDataLine = null;
                        try{
                            infoDataLine = ((DataLine.Info)allTLineInfos[jcbLines.getSelectedIndex()]);
                        }catch(Exception ex){
                            //ex.printStackTrace();
                        }
                        if(infoDataLine!=null){
                            for(AudioFormat audioFormat : infoDataLine.getFormats()){
                                jcbAudioFormat.addItem(audioFormat);
                                jcbAudioFormat.setEnabled(true);
                            }
                        }

                        int storeAudioFormat = NbPreferences.forModule(DriversPanel.class).getInt("AudioFormat", 0);
                        if(storeAudioFormat>=0 && jcbAudioFormat.getItemCount()>0){
                            jcbAudioFormat.setSelectedIndex(storeAudioFormat);
                        }

                    }
                }
            }catch(Exception ex){
                ex.printStackTrace();

            }
        }

         
        // TODO read settings and initialize GUI
        // Example:        
        // someCheckBox.setSelected(Preferences.userNodeForPackage(DriversPanel.class).getBoolean("someFlag", false));
        // or for org.openide.util with API spec. version >= 7.4:
        // someCheckBox.setSelected(NbPreferences.forModule(DriversPanel.class).getBoolean("someFlag", false));
        // or:
        // someTextField.setText(SomeSystemOption.getDefault().getSomeStringProperty());
    }

    void store() {
        // TODO store modified settings
        // Example:
        // Preferences.userNodeForPackage(DriversPanel.class).putBoolean("someFlag", someCheckBox.isSelected());
        // or for org.openide.util with API spec. version >= 7.4:
        // NbPreferences.forModule(DriversPanel.class).putBoolean("someFlag", someCheckBox.isSelected());
        // or:
        // SomeSystemOption.getDefault().setSomeStringProperty(someTextField.getText());

        NbPreferences.forModule(DriversPanel.class).putBoolean("Default", jtbUseDefault.isSelected());
        try{
            NbPreferences.forModule(DriversPanel.class).putInt("MixerDriver", jcbMixerDriver.getSelectedIndex());
        }catch(Exception ex){
            NbPreferences.forModule(DriversPanel.class).putInt("MixerDriver", -1);
        }

        try{
            NbPreferences.forModule(DriversPanel.class).putInt("TargetLine", jcbLines.getSelectedIndex());
        }catch(Exception ex){
            NbPreferences.forModule(DriversPanel.class).putInt("TargetLine", -1);
        }

        try{
            NbPreferences.forModule(DriversPanel.class).putInt("AudioFormat", jcbAudioFormat.getSelectedIndex());
        }catch(Exception ex){
            NbPreferences.forModule(DriversPanel.class).putInt("AudioFormat", -1);
        }
         
        if(!valid()){
            if(!jtbUseDefault.isSelected()){
                NotifyDialog.notify("Warning!", "This configuration may not work!", NotifyType.WARNING_MESSAGE, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent evt){

                        NotifyDescriptor nd = new NotifyDescriptor(
                                "Go to Menu->Tool->Options->Drivers and configure it!",
                                "Tip!",
                                NotifyDescriptor.PLAIN_MESSAGE,
                                NotifyDescriptor.INFORMATION_MESSAGE,
                                null,
                                NotifyDescriptor.INFORMATION_MESSAGE
                        );
                        DialogDisplayer.getDefault().notify(nd);
                    }
                });
            }
        }

        
        
    }

    boolean valid() {

        if(NbPreferences.forModule(DriversPanel.class).getInt("MixerDriver", 0)<0){
            return false;
        }
        if(NbPreferences.forModule(DriversPanel.class).getInt("TargetLine", 0)<0){
            return false;
        }
        if(NbPreferences.forModule(DriversPanel.class).getInt("AudioFormat", 0)<0){
            return false;
        }

        return true;
    } 

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JButton jbTest;
    private javax.swing.JComboBox jcbAudioFormat;
    private javax.swing.JComboBox jcbLines;
    private javax.swing.JComboBox jcbMixerDriver;
    private javax.swing.JToggleButton jtbUseDefault;
    // End of variables declaration//GEN-END:variables
}
